package com.demo.search.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.demo.search.model.NBAPlayer;
import com.demo.search.service.NBAPlayerService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Service;

/**
 * @author MoodAir
 */
@Service
public class NBAPlayerServiceImpl implements NBAPlayerService {
    
    private static final String NBA_INDEX = "nba2";
    private static final int START_OFFSET = 0;
    private static final int MAX_COUNT = 1000;
    @Resource
    private RestHighLevelClient client;
    
    @Override
    public boolean addPlayer(NBAPlayer player, String id) throws IOException {
        IndexRequest request = new IndexRequest(NBA_INDEX).id(id).source(beanToMap(player));
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        System.out.println(JSONObject.toJSON(response));
        return false;
    }
    
    @Override
    public Map<String, Object> getPlayer(String id) throws IOException {
        GetRequest getRequest = new GetRequest(NBA_INDEX, id);
        GetResponse response = client.get(getRequest, RequestOptions.DEFAULT);
        return response.getSource();
    }
    
    @Override
    public boolean updatePlayer(NBAPlayer player, String id) throws IOException {
        UpdateRequest request = new UpdateRequest(NBA_INDEX, id).doc(beanToMap(player));
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
        System.out.println(JSONObject.toJSON(response));
        return true;
    }
    
    @Override
    public boolean deletePlayer(String id) throws IOException {
        DeleteRequest request = new DeleteRequest(NBA_INDEX, id);
        client.delete(request, RequestOptions.DEFAULT);
        return true;
    }
    
    @Override
    public boolean deleteAllPlayer() throws IOException {
        DeleteByQueryRequest request = new DeleteByQueryRequest(NBA_INDEX);
        BulkByScrollResponse response = client.deleteByQuery(request, RequestOptions.DEFAULT);
        return true;
    }
    
    
    @Override
    public List<NBAPlayer> searchMatch(String key, String value) throws IOException {
        SearchRequest searchRequest = new SearchRequest(NBA_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchQuery(key, value));
        searchSourceBuilder.from(START_OFFSET);
        searchSourceBuilder.size(MAX_COUNT);
        searchRequest.source(searchSourceBuilder);
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(JSONObject.toJSON(response));
        
        SearchHit[] hits = response.getHits().getHits();
        List<NBAPlayer> playerList = new LinkedList<>();
        for (SearchHit hit : hits) {
            NBAPlayer player = JSONObject.parseObject(hit.getSourceAsString(), NBAPlayer.class);
            playerList.add(player);
        }
        
        return playerList;
    }
    
    @Override
    public List<NBAPlayer> searchTerm(String key, String value) throws IOException {
        SearchRequest searchRequest = new SearchRequest(NBA_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery(key, value));
        searchSourceBuilder.from(START_OFFSET);
        searchSourceBuilder.size(MAX_COUNT);
        searchRequest.source(searchSourceBuilder);
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(JSONObject.toJSON(response));
        
        SearchHit[] hits = response.getHits().getHits();
        List<NBAPlayer> playerList = new LinkedList<>();
        for (SearchHit hit : hits) {
            NBAPlayer player = JSONObject.parseObject(hit.getSourceAsString(), NBAPlayer.class);
            playerList.add(player);
        }
        
        return playerList;
    }
    
    
    @Override
    public List<NBAPlayer> searchMatchPrefix(String key, String value) throws IOException {
        SearchRequest searchRequest = new SearchRequest(NBA_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.prefixQuery(key, value));
        searchSourceBuilder.from(START_OFFSET);
        searchSourceBuilder.size(MAX_COUNT);
        searchRequest.source(searchSourceBuilder);
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        
        System.out.println(JSONObject.toJSON(response));
        
        SearchHit[] hits = response.getHits().getHits();
        List<NBAPlayer> playerList = new LinkedList<>();
        for (SearchHit hit : hits) {
            NBAPlayer player = JSONObject.parseObject(hit.getSourceAsString(), NBAPlayer.class);
            playerList.add(player);
        }
        
        return playerList;
    }
    
    
    @Override
    public void searchAggregation() throws IOException {
        
        //第一层队名
        TermsAggregationBuilder teamNameBuilder = AggregationBuilders.terms("teamName").field("teamName");
        
        //第二层队员国家
        TermsAggregationBuilder countryBuilder = AggregationBuilders.terms("country").field("country");
        
        //所属位置
        TermsAggregationBuilder positionsBuilder = AggregationBuilders.terms("position").field("position");
        
        //队员名字
        TermsAggregationBuilder displayNameBuilder = AggregationBuilders.terms("displayName")
                .field("displayName");
        
        ValueCountAggregationBuilder countAggregationBuilder = AggregationBuilders.count("count")
                .field("displayName");
        
        //按照层级塞入分组
        displayNameBuilder.subAggregation(countAggregationBuilder);
        positionsBuilder.subAggregation(displayNameBuilder);
        countryBuilder.subAggregation(positionsBuilder);
        teamNameBuilder.subAggregation(countryBuilder);
        
        //构建搜索
        SearchRequest searchRequest = new SearchRequest(NBA_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(teamNameBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        ParsedStringTerms teamName = searchResponse.getAggregations().get("teamName");
        
        //第一层 队名
        List<? extends Terms.Bucket> teamNameBuckets = teamName.getBuckets();
        for (Bucket teamNameBucket : teamNameBuckets) {
            //第一层根据对名字分组，循环打印对名字
            ParsedStringTerms country = teamNameBucket.getAggregations().get("country");
            
            //第二层根据国家
            List<? extends Bucket> countryBuckets = country.getBuckets();
            for (Bucket countryBucket : countryBuckets) {
                
                //第三层球队位置
                ParsedStringTerms position = countryBucket.getAggregations().get("position");
                List<? extends Bucket> positionBuckets = position.getBuckets();
                for (Bucket positionBucket : positionBuckets) {
                    
                    //第四层球员名字
                    ParsedStringTerms displayName = positionBucket.getAggregations().get("displayName");
                    for (Bucket bucket : displayName.getBuckets()) {
                        System.out.println(
                                "队名:" + teamNameBucket.getKey() + " 国家:" + countryBucket.getKey()
                                        + " 所打位置:" + positionBucket.getKey() + " 球员名字:" + bucket
                                        .getKey());
                    }
                    
                }
            }
        }
        
    }
    
    
    @Override
    public void searchScroll() {
        
        //有效期
        final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
        SearchRequest searchRequest = new SearchRequest(NBA_INDEX);
        searchRequest.scroll(scroll);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //分页数
        searchSourceBuilder.size(5);
        searchRequest.source(searchSourceBuilder);
        
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        String scrollId = searchResponse.getScrollId();
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        int page = 1;
        
        //第一页
        System.out.println("第一页");
        Arrays.stream(searchHits).map(SearchHit::getSourceAsString)
                .forEach(System.out::println);
        
        //条件循环下面所有页
        while (searchHits != null && searchHits.length > 0) {
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
            scrollRequest.scroll(scroll);
            try {
                searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }
            scrollId = searchResponse.getScrollId();
            searchHits = searchResponse.getHits().getHits();
            if (searchHits != null && searchHits.length > 0) {
                page++;
                System.out.println("第" + page + "页");
                Arrays.stream(searchHits).map(SearchHit::getSourceAsString)
                        .forEach(System.out::println);
            }
        }
        
        //清理游标快照
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
        clearScrollRequest.addScrollId(scrollId);
        ClearScrollResponse clearScrollResponse;
        try {
            clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
            boolean succeeded = clearScrollResponse.isSucceeded();
            System.out.println("清除滚屏是否成功:" + succeeded);
        } catch (IOException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        
    }
    
    
    /**
     * 高亮查询
     */
    @Override
    public void searchHighlight() throws IOException {

        
        //1.构建检索条件
        SearchRequest searchRequest = new SearchRequest(NBA_INDEX);
        
        //构建查询builder
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        HighlightBuilder.Field highlightTitle =
                new HighlightBuilder.Field("displayName.keyword");
        highlightTitle.highlighterType("unified");
        highlightBuilder.field(highlightTitle);
        HighlightBuilder.Field highlightUser = new HighlightBuilder.Field("user");
        highlightBuilder.field(highlightUser);
        sourceBuilder.highlighter(highlightBuilder);
        //执行结果
        sourceBuilder.highlighter(highlightBuilder);
        searchRequest.source(sourceBuilder);
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        
        //获取结果
        SearchHit[] hits = response.getHits().getHits();
        
    
        ArrayList<Map<String, Object>> mapArrayList = new ArrayList<>();
    
        for (SearchHit hit : hits) {
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField displayName = highlightFields.get("displayName");
            StringBuilder newName = new StringBuilder();
            if (displayName != null) {
                //获取该高亮字段的高亮信息
                Text[] fragments = displayName.getFragments();
                //将前缀、关键词、后缀进行拼接
                for (Text fragment : fragments) {
                    newName.append(fragment);
                }
            }
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            //将高亮后的值替换掉旧值
            sourceAsMap.put("productName", newName.toString());
            mapArrayList.add(sourceAsMap);
        }
    
        System.out.println(JSONObject.toJSONString(mapArrayList, true));
    }
    
    public static <T> Map<String, Object> beanToMap(T bean) {
        Map<String, Object> map = new HashMap<>();
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                if (beanMap.get(key) != null) {
                    map.put(key + "", beanMap.get(key));
                }
            }
        }
        return map;
    }
}
